package com.abc.config.security;

import com.abc.model.Result;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.stream.Collectors;

/**
 * 负责处理登录请求，如果请求url是'/login'，
 * 从请求体中解析json，解析出'username'、'password'两个字段，
 * 然后进行登录，登录成功后会向SecurityContext中存入一个Authentication
 */
@Slf4j
public class LoginFilter extends UsernamePasswordAuthenticationFilter {

    @Autowired
    private JwtTokenService jwtTokenService;

    public LoginFilter() {
        this("username","password");
    }

    public LoginFilter(String usernameParameter, String passwordParameter) {
        setFilterProcessesUrl("/login");
        setUsernameParameter(usernameParameter);
        setPasswordParameter(passwordParameter);
        setAuthenticationSuccessHandler(successHandler());
        setAuthenticationFailureHandler(failureHandler());
    }

    /**
     * 认证成功时触发的处理方法
     */
    public AuthenticationSuccessHandler successHandler(){
        return (req,res,auth) ->{

            String token = jwtTokenService.createToken(auth.getName(), auth.getAuthorities()
                    .stream()
                    .map(GrantedAuthority::getAuthority)
                    .collect(Collectors.toList()));

            Result json = Result.success("login").msg("登录成功").data("token",token);
            res.setStatus(200);
            res.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            res.getWriter().write(json.toString());
        };
    }

    /**
     * 认证失败时触发的处理方法
     */
    public AuthenticationFailureHandler failureHandler(){
        return (req,res,ex) ->{
            Result json = Result.error("login").msg("登录失败").data("exception",ex.getMessage());
            res.setStatus(200);
            res.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            res.getWriter().write(json.toString());
        };
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        log.info("==> {} {} 过滤器尝试登录...",request.getMethod(), request.getRequestURL());
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }

        String username = "";
        String password = "";

        try {
            BufferedReader reader = request.getReader();
            String body = reader.lines().collect(Collectors.joining("\n"));
            JSONObject json = JSON.parseObject(body);
            username = json.getString(getUsernameParameter());
            password = json.getString(getPasswordParameter());
        } catch (IOException e) {
            throw new AuthenticationServiceException("读取请求体内容时出错");
        }

        username = username.trim();

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }

}
